package sync

import (
	"accx/appFramework/m_config"
	"accx/utils/httpClient"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cihub/seelog"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

type ResponseSucMsg struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

type DynamicData struct {
	DeviceId string `json:"deviceId"`
	Group    string `json:"group"`
	Node     string `json:"node"`
	UserList []struct {
		Impi             string   `json:"impi"`
		Impu             []string `json:"impu"`
		UserState        string   `json:"userState"`
		UpdatedTimpstamp string   `json:"updatedTimpstamp"`
		ScscfUri         string   `json:"scscfUri"`
		SmscPeerAddr     string   `json:"smscPeerAddr"`
	} `json:"userList"`
}

// Service 定义服务结构体，包含IP和端口
type Service struct {
	IP   string
	Port int
}

func (data *DynamicData) SendUDM() error {
	//调用retry函数，最多尝试5次，每次间隔5秒
	err := retry(func(d interface{}) error {
		return retryableOperation(d.(DynamicData))
	}, *data, 5, 5*time.Second)
	if err != nil {
		seelog.Error("Operation failed: ", err)
	} else {
		seelog.Debug("Operation succeeded")
	}
	return nil
}

func retryableOperation(data DynamicData) error {
	var resultSet ResponseSucMsg
	marshalData, _ := json.Marshal(data)
	// 发送HTTP请求
	req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:8401/v1/udmDynamicData", m_config.MyAccxData.ServiceIp), bytes.NewReader(marshalData))
	if err != nil {
		seelog.Error("code request data error: ", err)
		return err
	}

	cli := httpClient.NewHttp2Client()
	resp, err := cli.DoRequest(req)
	if err != nil {
		seelog.Error("new request error: ", err)
		return err
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	err = json.Unmarshal(respBody, &resultSet)
	if err != nil {
		seelog.Error("decode response data error: ", err)
		return err
	}
	if resultSet.Code != http.StatusOK || resultSet.Code != http.StatusOK {
		return errors.New(resultSet.Message)
	}
	return nil
}

// retry 执行一个可能会失败的操作，并在失败时重试
func retry(operation func(interface{}) error, data interface{}, maxAttempts int, delay time.Duration) error {
	var err error
	for i := 1; i <= maxAttempts; i++ {
		err = operation(data)
		if err == nil {
			return nil // 成功，返回nil
		}
		// 失败，等待一段时间后再重试
		time.Sleep(delay)
	}
	// 所有尝试都失败了，返回最后一个错误
	return fmt.Errorf("after %d attempts, last error: %v", maxAttempts, err)
}

// GetUDM 获取udm全量数据

func (data *DynamicData) GetUDM() error {
	var resultSet ResponseSucMsg
	var dyData DynamicData
	req, err := http.NewRequest("GET", fmt.Sprintf("http://%s:8401/v1/udmDynamicData", m_config.MyAccxData.ServiceIp), nil)
	if err != nil {
		seelog.Error("code request data error: ", err)
		return err
	}

	cli := httpClient.NewHttp2Client()
	resp, err := cli.DoRequest(req)
	if err != nil {
		seelog.Error("new request error: ", err)
		return err
	}
	respBody, _ := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	err = json.Unmarshal(respBody, &resultSet)
	if err != nil {
		seelog.Error("decode response data error: ", err)
		return err
	}
	if resultSet.Code != http.StatusOK || resultSet.Code != http.StatusOK {
		return errors.New(resultSet.Message)
	}
	dyData, ok := resultSet.Data.(DynamicData)
	if !ok {
		return errors.New("failed to assert type to DynamicData")
	}
	*data = dyData
	return nil
}

// BroadcastRequest sends the request to all services concurrently and collects the responses.
func (data *DynamicData) BroadcastRequest(services []Service) ([]ResponseSucMsg, error) {
	var wg sync.WaitGroup
	responses := make(chan ResponseSucMsg, len(services)) // Buffered channel to hold responses.
	errors := make(chan error, len(services))             // Channel to hold errors.

	// Function to handle a single request to a service.
	sendRequest := func(service Service) {
		defer wg.Done()

		jsonData, err := json.Marshal(*data)
		if err != nil {
			seelog.Error("code request data error: ", err)
			errors <- err
			return
		}

		req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:%d/v1/accxDynamicData", service.IP, service.Port), bytes.NewReader(jsonData))
		if err != nil {
			seelog.Error("new request error: ", err)
			errors <- err
			return
		}

		// Use a client with a timeout.
		cli := httpClient.NewHttp2Client()
		resp, err := cli.DoRequest(req)
		if err != nil {
			seelog.Error("handle request error: ", err)
			errors <- err
			return
		}
		defer resp.Body.Close()

		var resultData map[string]interface{}
		if resp.StatusCode == http.StatusOK {
			body, _ := ioutil.ReadAll(resp.Body)
			err = json.Unmarshal(body, &resultData)
			if err != nil {
				seelog.Error("decode response data error: ", err)
				errors <- err
				return
			}
		}

		responses <- ResponseSucMsg{Code: resp.StatusCode, Data: resultData, Message: ""}
	}

	// Start goroutines for each service.
	for _, service := range services {
		wg.Add(1)
		go sendRequest(service)
	}

	// Wait for all goroutines to finish.
	go func() {
		wg.Wait()
		close(responses)
		close(errors)
	}()

	// Collect responses and errors.
	var result []ResponseSucMsg
	var errList []error
	for {
		select {
		case resp, ok := <-responses:
			if !ok {
				// Channel closed.
				break
			}
			result = append(result, resp)
		case err, ok := <-errors:
			if !ok {
				// Channel closed.
				break
			}
			errList = append(errList, err)
		}
	}

	if len(errList) > 0 {
		// Handle errors (e.g., log them, return the first one, aggregate them).
		for _, e := range errList {
			seelog.Error("Error: ", e)
		}
		return nil, errList[0] // Returning the first error as an example.
	}
	return result, nil
}
